#include "dx8_image_cache.h"
#include "dx8_graphics.h"

#include "application/game_application.h"

#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#include <cassert>


DX8ImageCache::DX8ImageCache() : 
	ImageCache(),
	m_pRenderTarget(NULL),
	m_pRenderTexture(0),
	m_pRenderSurface(0)
{
	GetGraphics()->AddImageCache(this);
}

DX8ImageCache::~DX8ImageCache()
{
	if (GetGraphics())
	{
		GetGraphics()->DropImageCache(this);
		Flush();
	}
}

void		DX8ImageCache::Flush()
{
	UTIL_SAFE_RELEASE(m_pRenderTarget);
	UTIL_SAFE_RELEASE(m_pRenderSurface);
	UTIL_SAFE_RELEASE(m_pRenderTexture);
}
void		DX8ImageCache::Recreate()
{
	_LOG(MSG_INIT, "recreating render target " << m_Width << "x" << m_Height);
	Create(m_Width, m_Height, m_Depth);
}


void		DX8ImageCache::Create(int	Width, int Height, int Depth)
{
	m_Width = Width;
	m_Height = Height;
	m_Depth = Depth;

	D3DFORMAT Format = D3DFMT_A8R8G8B8;
	if (Depth == 24) Format = D3DFMT_R8G8B8; else
	if (Depth == 16) Format = D3DFMT_R5G6B5;
	if (Depth == 8)  Format = D3DFMT_A8;

	// PIERRE: Required to work on all card, will get a compatible device format if the one we picked wont do it
	HRESULT hr;
	hr = D3DXCheckTextureRequirements(GetDXGraphics()->GetDevice(), &m_Width, &m_Height, NULL, D3DUSAGE_RENDERTARGET, &Format, D3DPOOL_DEFAULT);
	if (FAILED(hr)) {
		_LOG(MSG_WARNING, "unable to check texture requirements for render to texture " << Width << ", " << Height);
		return;
	}

	hr = D3DXCreateTexture(
		GetDXGraphics()->GetDevice(), 
		m_Width, m_Height, 
		1, 
		D3DUSAGE_RENDERTARGET, 
		Format, 
		D3DPOOL_DEFAULT, 
		&m_pRenderTexture);
	if (FAILED(hr))
	{
		_LOG(MSG_WARNING, "unable to create render to texture " << Width << ", " << Height);
		return;
	}
    hr = m_pRenderTexture->GetSurfaceLevel(0, &m_pRenderSurface);
	if (FAILED(hr))
	{
		_LOG(MSG_WARNING, "unable to recover surface from R2T  [" << Width << ", " << Height << "]");
		return;
	}

	D3DSURFACE_DESC desc;
    m_pRenderSurface->GetDesc(&desc);

	hr = D3DXCreateRenderToSurface(
		GetDXGraphics()->GetDevice(),
		desc.Width, desc.Height,
		desc.Format,		// allow 16 bit formats as well?
		false,
		D3DFMT_UNKNOWN, // depth/stencil value (D3DFMT_UNKNOWN)
		&m_pRenderTarget);
	if (FAILED(hr))
	{
		_LOG(MSG_WARNING, "Unable to create render target object [" << Width << ", " << Height << "]");
		return;
	}
}

void		DX8ImageCache::SetTextureStage(int Stage)
{
	if (m_pRenderTexture)
	{
		GetDXGraphics()->GetDevice()->SetTexture(Stage, m_pRenderTexture);
	}
}

void		DX8ImageCache::ClearCache(COLOR c)
{
	if (m_pRenderTarget)
	{
		GetDXGraphics()->GetDevice()->Clear(0, NULL, D3DCLEAR_TARGET, c, 0.0f, 0);
	}
}

bool		DX8ImageCache::BeginCache()
{
	if (GetDXGraphics()->HasInvalidState()) return false;
	if (!m_pRenderTarget)
	{
		Recreate();
		// ADRIAN: don't return false if Recreate was successful
		if (!m_pRenderTarget)
			return false;
	}
	math::Viewport vp;
	vp.X = 0;
	vp.Y = 0;
	
	vp.Width = g_pApp->GetHandler()->ScreenWidth();
	vp.Height = g_pApp->GetHandler()->ScreenHeight();
	vp.MinZ = 0;
	vp.MaxZ = 1;
	GetGraphics()->SetViewport(vp);


	// Stop winwod-offsets from affecting the cache
	m_CachedOffsetX = Graphics::s_PixelOffsetX;
	m_CachedOffsetY = Graphics::s_PixelOffsetY;
	Graphics::s_PixelOffsetX = 0;
	Graphics::s_PixelOffsetY = 0;

	assert(m_pRenderTarget);
	GetGraphics()->Start2D();
	HRESULT hr = m_pRenderTarget->BeginScene(m_pRenderSurface, NULL);
	assert(SUCCEEDED(hr));
	return true;
}
void		DX8ImageCache::EndCache()
{
	if (!m_pRenderTarget) return;
	m_pRenderTarget->EndScene();

	// restore winwod-offsets 
	Graphics::s_PixelOffsetX = m_CachedOffsetX;
	Graphics::s_PixelOffsetY = m_CachedOffsetY;

}